package com.huiyun.project.pay.biz.handler.pay;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.system.SystemUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.enums.TradeType;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.WxPayApiConfig;
import com.ijpay.wxpay.WxPayApiConfigKit;
import com.ijpay.wxpay.model.RefundModel;
import com.ijpay.wxpay.model.UnifiedOrderModel;
import com.huiyun.project.common.core.domain.R;
import com.huiyun.project.common.core.util.WebUtils;
import com.huiyun.project.common.sequence.IdUtils;
import com.huiyun.project.pay.biz.dto.H5SceneInfoDto;
import com.huiyun.project.pay.biz.dto.PayOrderDto;
import com.huiyun.project.pay.biz.entity.PayChannel;
import com.huiyun.project.pay.biz.entity.PayOrder;
import com.huiyun.project.pay.biz.entity.RefundOrder;
import com.huiyun.project.pay.biz.kit.PayConfigKit;
import com.huiyun.project.pay.biz.service.PayChannelService;
import com.huiyun.project.pay.biz.service.PayOrderService;
import com.huiyun.project.pay.biz.service.RefundOrderService;
import com.huiyun.project.pay.biz.utils.StringUtils;
import com.huiyun.project.pay.client.vo.RefundReq;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Date;
import java.util.Map;

@Component
@Slf4j
public class WxPayHandler {
    @Autowired
    private PayChannelService payChannelService;
    @Autowired
    private PayOrderService payOrderService;
    @Autowired
    private RefundOrderService refundOrderService;

    /**
     * 微信小程序支付
     */
    public R<String> miniAppPay(PayOrderDto payOrderDto) {
        log.info("-------------[商户统一下单]小程序统一下单开始-------------");
        WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getApiConfig(payOrderDto.getChannelAppId());
        Map<String, String> params = UnifiedOrderModel
                .builder()
                .appid(wxPayApiConfig.getAppId())
                .mch_id(wxPayApiConfig.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .body(payOrderDto.getBody())
                .attach(payOrderDto.getExtra())
                .out_trade_no(payOrderDto.getMchOrderNo())
                .total_fee(Convert.toStr(payOrderDto.getAmount()))
                .fee_type((payOrderDto.getCurrency()) + "".toUpperCase())
                .spbill_create_ip(payOrderDto.getClientIp())
                .notify_url(getNotifyUrl(wxPayApiConfig.getDomain(), payOrderDto.getChannelAppId()))
                .trade_type(TradeType.JSAPI.getTradeType())
                .openid(payOrderDto.getOpenId())
                .build()
                .createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);
        log.debug("-------------[商户统一下单]小程序统一下单请求参数{}-------------", JSON.toJSONString(params));
        String xmlResult = WxPayApi.pushOrder(false, params);

        log.info(xmlResult);
        log.debug("-------------[商户统一下单]小程序统一下单结果信息{}-------------", xmlResult);
        Map<String, String> result = WxPayKit.xmlToMap(xmlResult);

        String returnCode = result.get("return_code");
        String returnMsg = result.get("return_msg");
        String resultCode = result.get("result_code");
        String errorMsg = result.get("err_code_des");
        if (!WxPayKit.codeIsOk(returnCode)) {
            return R.failed(returnMsg);
        }
        if (!WxPayKit.codeIsOk(resultCode)) {
            return R.failed(errorMsg);
        }
        // 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回
        String prepayId = result.get("prepay_id");
        Map<String, String> packageParams = WxPayKit.miniAppPrepayIdCreateSign(wxPayApiConfig.getAppId(), prepayId,
                wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);
        String jsonStr = JSON.toJSONString(packageParams);
        log.debug("-------------[商户统一下单]小程序下单返回的支付参数{}-------------", jsonStr);
        return R.ok(jsonStr);
    }

    /**
     * 微信APP支付
     */
    public R<String> appPay(PayOrderDto payOrderDto) {
        WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getApiConfig(payOrderDto.getChannelAppId());
        Map<String, String> params = UnifiedOrderModel
                .builder()
                .appid(wxPayApiConfig.getAppId())
                .mch_id(wxPayApiConfig.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .body(payOrderDto.getBody())
                .attach(payOrderDto.getExtra())
                .out_trade_no(payOrderDto.getMchOrderNo())
                .total_fee(Convert.toStr(payOrderDto.getAmount()))
                .fee_type((payOrderDto.getCurrency()) + "".toUpperCase())
                .spbill_create_ip(payOrderDto.getClientIp())
                .notify_url(getNotifyUrl(wxPayApiConfig.getDomain(), payOrderDto.getChannelAppId()))
                .trade_type(TradeType.APP.getTradeType())
                .build()
                .createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);

        String xmlResult = WxPayApi.pushOrder(false, params);

        log.info(xmlResult);
        Map<String, String> result = WxPayKit.xmlToMap(xmlResult);

        String returnCode = result.get("return_code");
        String returnMsg = result.get("return_msg");
        String resultCode = result.get("result_code");
        String errorMsg = result.get("err_code_des");
        if (!WxPayKit.codeIsOk(returnCode)) {
            return R.failed(returnMsg);
        }

        if (!WxPayKit.codeIsOk(resultCode)) {
            return R.failed(errorMsg);
        }
        // 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回
        String prepayId = result.get("prepay_id");

        Map<String, String> packageParams = WxPayKit.appPrepayIdCreateSign(wxPayApiConfig.getAppId(), wxPayApiConfig.getMchId(), prepayId,
                wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);

        String jsonStr = JSON.toJSONString(packageParams);
        log.info("返回apk的参数:" + jsonStr);
        return R.ok(jsonStr);
    }


    /**
     * 退款操作
     *
     * @param refundReq
     * @return
     */
    public String refund(RefundReq refundReq, PayChannel payChannel) {
        PayConfigKit.init(payChannel.getChannelAppId());
        PayOrder payOrder = payOrderService.getByMchOrderNo(refundReq.getMchId(), refundReq.getMchOrderNo());
        Assert.notNull(payOrder, "无效的退款订单信息");
        WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getApiConfig(payChannel.getChannelAppId());
        //生成退款订单(同一个订单可以生成多次,但是同一个退款单只能生成一次)
        RefundOrder refundOrder = new RefundOrder();
        refundOrder.setId(Convert.toStr(IdUtils.getId()));
        refundOrder.setChannelId(payOrder.getChannelId());
        refundOrder.setClientIp(WebUtils.getIP());
        refundOrder.setCurrency(refundReq.getCurrency());
        refundOrder.setMchId(refundReq.getMchId());
        refundOrder.setNotifyUrl(getRefundNotifyNotifyUrl(wxPayApiConfig.getDomain(), payChannel.getChannelAppId()));
        refundOrder.setMchRefundNo(refundReq.getMchRefundNo());
        refundOrder.setPayOrderId(payOrder.getId());
        refundOrder.setChannelMchId(payOrder.getChannelMchId());
        refundOrder.setPayAmount(payOrder.getAmount());
        refundOrder.setRefundAmount(refundReq.getRefundAmount());
        refundOrder.setOptId(refundReq.getOptId());
        refundOrder.setCreateTime(new Date());
        refundOrderService.save(refundOrder);

        Map<String, String> params = RefundModel.builder()
                .appid(wxPayApiConfig.getAppId())
                .mch_id(wxPayApiConfig.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .transaction_id(payOrder.getChannelOrderNo())
                .out_trade_no(refundReq.getMchOrderNo())
                .out_refund_no(refundOrder.getId())
                .refund_desc(refundReq.getRefundDesc())
                .total_fee(Convert.toStr(payOrder.getAmount()))
                .refund_fee(Convert.toStr(refundReq.getRefundAmount()))
                .notify_url(getRefundNotifyNotifyUrl(wxPayApiConfig.getDomain(), payChannel.getChannelAppId()))
                .build()
                .createSign(wxPayApiConfig.getPartnerKey(), SignType.MD5);
        String certPath = wxPayApiConfig.getCertPath();
        if (SystemUtil.getOsInfo().isWindows()) {
            certPath = "D://" + StringUtils.replace(certPath, "{channelMchId}", payChannel.getChannelMchId());
        } else {
            certPath = StringUtils.replace(certPath, "{channelMchId}", payChannel.getChannelMchId());
        }
        return WxPayApi.orderRefund(false, params, certPath, wxPayApiConfig.getMchId());
    }


    /**
     * 微信h5支付
     *
     * @throws IOException
     */
    public String wapPay(PayOrderDto payOrderDto) {
        PayChannel payChannel = payChannelService.getByMchIdAndChannelAppId(payOrderDto.getMchId(), payOrderDto.getChannelAppId());
        JSONObject obj = JSONObject.parseObject(payChannel.getParam());
        H5SceneInfoDto sceneInfo = new H5SceneInfoDto();
        H5SceneInfoDto.H5 h5_info = new H5SceneInfoDto.H5();
        h5_info.setType("Wap");
        //此域名必须在商户平台--"产品中心"--"开发配置"中添加
        h5_info.setWap_url(obj.getString("wapUrl)"));
        h5_info.setWap_name(obj.getString("wapName"));
        sceneInfo.setH5Info(h5_info);

        WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

        Map<String, String> params = UnifiedOrderModel
                .builder()
                .appid(wxPayApiConfig.getAppId())
                .mch_id(wxPayApiConfig.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .body(payOrderDto.getBody())
                .attach(payOrderDto.getExtra())
                .out_trade_no(WxPayKit.generateStr())
                .total_fee(Convert.toStr(payOrderDto.getAmount()))
                .fee_type((payOrderDto.getCurrency()) + "".toUpperCase())
                .spbill_create_ip(payOrderDto.getClientIp())
                .notify_url(getNotifyUrl(wxPayApiConfig.getDomain(), payOrderDto.getChannelAppId()))
                .trade_type(TradeType.MWEB.getTradeType())
                .scene_info(JSON.toJSONString(sceneInfo))
                .build()
                .createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);

        String xmlResult = WxPayApi.pushOrder(false, params);
        log.info(xmlResult);

        Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
        String returnCode = result.get("return_code");
        String returnMsg = result.get("return_msg");
        String result_code = result.get("result_code");
        String errorMsg = result.get("err_code_des");
        if (!WxPayKit.codeIsOk(returnCode)) {
            throw new RuntimeException(returnMsg);
        }

        if (!WxPayKit.codeIsOk(result_code)) {
            throw new RuntimeException(errorMsg);
        }
        // 以下字段在return_code 和result_code都为SUCCESS的时候有返回

        String prepayId = result.get("prepay_id");
        String webUrl = result.get("mweb_url");

        log.info("prepay_id:" + prepayId + " mweb_url:" + webUrl);
        return webUrl;
    }


    /**
     * 公众号支付
     */
    public R<String> mpPay(PayOrderDto payOrderDto) {
        if (StrUtil.isEmpty(payOrderDto.getOpenId())) {
            return R.failed("openId is null");
        }
        if (payOrderDto.getAmount() != null) {
            return R.failed("请输入数字金额");
        }
        WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();

        Map<String, String> params = UnifiedOrderModel
                .builder()
                .appid(wxPayApiConfig.getAppId())
                .mch_id(wxPayApiConfig.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .body(payOrderDto.getBody())
                .attach(payOrderDto.getExtra())
                .out_trade_no(WxPayKit.generateStr())
                .total_fee(Convert.toStr(payOrderDto.getAmount()))
                .fee_type((payOrderDto.getCurrency()) + "".toUpperCase())
                .spbill_create_ip(payOrderDto.getClientIp())
                .notify_url(getNotifyUrl(wxPayApiConfig.getDomain(), payOrderDto.getChannelAppId()))
                .trade_type(TradeType.JSAPI.getTradeType())
                .openid(payOrderDto.getOpenId())
                .build()
                .createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);

        String xmlResult = WxPayApi.pushOrder(false, params);
        log.info(xmlResult);

        Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
        String returnCode = result.get("return_code");
        String returnMsg = result.get("return_msg");
        String resultCode = result.get("result_code");
        String errorMsg = result.get("err_code_des");
        if (!WxPayKit.codeIsOk(returnCode)) {
            return R.failed(returnMsg);
        }
        if (!WxPayKit.codeIsOk(resultCode)) {
            return R.failed(errorMsg);
        }

        // 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回

        String prepayId = result.get("prepay_id");

        Map<String, String> packageParams = WxPayKit.prepayIdCreateSign(prepayId, wxPayApiConfig.getAppId(),
                wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);

        String jsonStr = JSON.toJSONString(packageParams);
        return R.ok(jsonStr);
    }


    /**
     * 支付通知
     *
     * @return
     */
    public String getNotifyUrl(String domain, String channelAppId) {
        return domain.concat("/gw/pay/wxPay/payNotify/" + channelAppId);
    }

    /**
     * 退款通知
     *
     * @return
     */
    public String getRefundNotifyNotifyUrl(String domain, String channelAppId) {
        return domain.concat("/gw/pay/wxPay/refundNotify/" + channelAppId);
    }

}
